/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2012/07/11
* File: SkyboxSceneNode.cpp
* Blog: http://www.cnblogs.com/zengqh/
**/

#include "../RenderSystem/MaterialData.h"
#include "../RenderSystem/Texture.h"
#include "../RenderSystem/RenderSystem.h"
#include "CameraSceneNode.h"
#include "../Model/cube.h"
#include "SceneManager.h"
#include "../RenderSystem/GraphicsBuffer.h"
#include "../RenderSystem/EffectService.h"
#include "SkyboxSceneNode.h"

SkyboxSceneNode::SkyboxSceneNode(
				SceneNode* parent, 
				SceneManager* scene_mgr, 
				s32 id,
				s32 type,
				Texture* top,
				Texture* bottom,
				Texture* left,
				Texture* right,
				Texture* front,
				Texture* back)
				: SceneNode(parent, scene_mgr, id, type)
{
	_texture_list[0] = front;
	_texture_list[1] = back;
	_texture_list[2] = left;
	_texture_list[3] = right;
	_texture_list[4] = top;
	_texture_list[5] = bottom;

	initMaterial();
	initGraphicsBuffer();

#ifdef GLSL_SUPPORT_ENABLE
	initEffectService();
#endif
}

SkyboxSceneNode::~SkyboxSceneNode()
{

}

void SkyboxSceneNode::initMaterial()
{
	for(int i = 0; i < 6; ++i)
	{
		MaterialData& material_data = _sky_box_material[i];

		material_data._lighting = false;

		material_data._texture_layer_list[0]._texture = _texture_list[i];
		material_data._texture_layer_list[0]._is_linear_filter = true;
		material_data._texture_layer_list[0]._texture_wrap_u = ETC_CLAMP_TO_EDGE;
		material_data._texture_layer_list[0]._texture_wrap_v = ETC_CLAMP_TO_EDGE;
	}
}

void SkyboxSceneNode::initGraphicsBuffer()
{
	RenderSystem* render_system = _scene_mgr->getRenderSystem();

	_vbo_buffer = render_system->createGraphicsBuffer(BU_Static, BA_Read_Write, BS_VERTEX_BUFFER);
	_idx_buffer = render_system->createGraphicsBuffer(BU_Static, BA_Read_Write, BS_INDEX_BUFFER);

	/* set the vbo vertex */
	_vbo_buffer->resize(sizeof(cube_vertex_content));
	GraphicsBuffer::Mapper vbo_mapper(*_vbo_buffer, BA_Write_Only);

	u8* vbo_raw_buffer = vbo_mapper.Pointer<u8>();
	memcpy(vbo_raw_buffer, cube_vertex_content, _vbo_buffer->size());

	/* set the vbo index */
	_idx_buffer->resize(sizeof(cube_index_content));
	GraphicsBuffer::Mapper idx_mapper(*_idx_buffer, BA_Write_Only);

	u8* idx_raw_buffer = idx_mapper.Pointer<u8>();
	memcpy(idx_raw_buffer, cube_index_content, _idx_buffer->size());
}

void SkyboxSceneNode::initEffectService()
{
	char* vs = textReadFromFile("../Media/skybox/skybox.vs");
	char* ps = textReadFromFile("../Media/skybox/skybox.ps");

	RenderSystem* render_system = _scene_mgr->getRenderSystem();

	Effect* effect = render_system->createEffect();

	effect->load(vs, ps);
	_skybox_effect_service = new SkyboxEffectiveService(effect, render_system);
}

void SkyboxSceneNode::render()
{
	RenderSystem* render_system = _scene_mgr->getRenderSystem();
	CameraSceneNode* camera_node = _scene_mgr->getActiveCameraNode();

	f32 near_plane = camera_node->getNearPlane();
	f32 far_plane = camera_node->getFarPlane();

	f32 view_distance = (near_plane + far_plane) * 0.5f;

	glm::mat4 translate = glm::translate(glm::mat4(1.0f), camera_node->getEyePos());

	glm::mat4 scale = glm::scale(translate, glm::vec3(view_distance, view_distance, view_distance));

	render_system->setTransform(TS_World, scale);
	render_system->setGraphicsBuffer(BS_VERTEX_BUFFER, _vbo_buffer);
	render_system->setGraphicsBuffer(BS_INDEX_BUFFER, _idx_buffer);

	for(int i = 0; i < 6; ++i)
	{
#ifdef GLSL_SUPPORT_ENABLE
		render_system->setEffectService(_skybox_effect_service);
#endif

		render_system->setMaterial(&_sky_box_material[i]);

		render_system->setFVF(FVF_V3F_N3F_T2F);

		render_system->drawPrimitiveIndex(6, PT_TRIANGLES, i * 6, true);
	}
}

SkyboxEffectiveService::SkyboxEffectiveService(Effect* effect, RenderSystem* render_system)
: EffectService(effect)
, _render_system(render_system)
{
	
}

SkyboxEffectiveService::~SkyboxEffectiveService()
{

}

void SkyboxEffectiveService::applyMatrix()
{
	const glm::mat4& m = _render_system->getTransform(TS_World);
	const glm::mat4& v = _render_system->getTransform(TS_View);
	const glm::mat4& p = _render_system->getTransform(TS_Projection);

	glm::mat4 mvp = p * v * m;

	_effect->setMatrix("g_modelViewProjection", glm::value_ptr(mvp));
}
void SkyboxEffectiveService::applyMaterial()
{
	const MaterialData& data = _render_system->getMaterial();

	Texture* texture = data.getTexture(0);

	texture->active(0);

	_effect->setInt("texture", 0);
}
void SkyboxEffectiveService::applyLight()
{
	/* dummy */
}

void SkyboxEffectiveService::active()
{
	if (_effect)
	{
		_effect->active();
	}
}


s32  SkyboxEffectiveService::getVertexHandle()
{
	return _effect->getAttributeHandle("m_vertex");
}

s32  SkyboxEffectiveService::getNormalHandle()
{
	return _effect->getAttributeHandle("m_normal");
}

s32  SkyboxEffectiveService::getTexcoordHandle()
{
	return _effect->getAttributeHandle("m_texcoord");
}

s32  SkyboxEffectiveService::getColorHandle()
{
	return -1;
}
